package org.yx.controller;

import com.jfinal.upload.UploadFile;
import com.xiaoleilu.hutool.collection.CollUtil;
import com.xiaoleilu.hutool.date.DateUtil;
import com.xiaoleilu.hutool.io.FileUtil;
import com.xiaoleilu.hutool.io.IoUtil;
import com.xiaoleilu.hutool.io.resource.ResourceUtil;
import com.xiaoleilu.hutool.thread.GlobalThreadPool;
import com.xiaoleilu.hutool.util.ImageUtil;
import com.xiaoleilu.hutool.util.RandomUtil;
import com.xiaoleilu.hutool.util.StrUtil;
import io.jboot.web.controller.JbootController;
import io.jboot.web.controller.annotation.RequestMapping;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.PutObjectOptions;
import org.yx.exception.BizException;
import org.yx.models.MbasFilDef;
import org.yx.service.bas.save_file_info.BasSaveFileInfoIn;
import org.yx.service.bas.save_file_info.BasSaveFileInfoService;
import org.yx.service.bas.view_file_info.BasViewFileInfoIn;
import org.yx.service.bas.view_file_info.BasViewFileInfoService;
import org.yx.service.core.ServiceOut;
import org.yx.utils.Config;
import org.yx.utils.ServiceBeanUtil;

import javax.annotation.PostConstruct;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.List;

@RequestMapping("/file")
public class FileController extends JbootController {
    MinioClient client = null;
    boolean deleting = false;
    private final String bucket = Config.getString("file.minio.bucket");

    @PostConstruct
    public void init() {
        try {
            client = new MinioClient(Config.getString("file.minio.endpoint"),
                    Config.getString("file.minio.accesskey"),
                    Config.getString("file.minio.secretkey"));
        } catch (Exception ignored) {
        }
    }

    private void delOldFiles(String filePath) {
        if (deleting) {
            return;
        }

        try {
            deleting = true;

            List<File> files = FileUtil.loopFiles(filePath);//ResourceUtil.getResource("attachment").getPath());
            for (File file : files) {
                if (FileUtil.lastModifiedTime(file).compareTo(DateUtil.offsetHour(new Date(), -1).toJdkDate()) <= 0) {
                    FileUtil.del(file);
                }
            }
        } catch (Exception ignored) {
        } finally {
            deleting = false;
        }
    }

    // 文件上传
    public void upload() {
        UploadFile uploadFile = getFile();//这个要在普通getPara之前调用，getPara才能获取值
        final String path = uploadFile.getUploadPath();
        GlobalThreadPool.execute(new Runnable() {
            public void run() {
                synchronized (this) {
                    delOldFiles(path);
                }
            }
        });

        ServiceOut so = new ServiceOut();
        if (client == null) {
            so.setRspCode("BAS-0014");
            so.setRspDesc("上传文件失败。无法初始化文件服务客户端。");

            renderJson(so);

            return;
        }

        String group = getPara("group");
        String mode = getPara("mode");

        String fileId = RandomUtil.simpleUUID();
        try {
            client.putObject(bucket,
                    (StrUtil.isNotBlank(group) ? (group + "/") : "") + fileId,
                    uploadFile.getFile().getPath(),
                    new PutObjectOptions(getFile().getFile().length(), 104857600L));//100Mb
        } catch (Exception ex) {
            so.setRspCode("BAS-0014");
            so.setRspDesc("上传文件失败。" + ex.getMessage());

            renderJson(so);

            return;
        }

        if (StrUtil.equalsIgnoreCase("S", mode)) {
            try {
                MbasFilDef fileDef = new MbasFilDef();
                fileDef.setFileId(fileId);
                fileDef.setFileGroup(bucket);
                fileDef.setFileName(uploadFile.getOriginalFileName());
                fileDef.setFilePath(client.getObjectUrl(bucket, (StrUtil.isNotBlank(group) ? (group + "/") : "") + fileId));
                fileDef.setFileSize((int) uploadFile.getFile().length());
                fileDef.setFileStatus('N');
                fileDef.setFileType('T');

                BasSaveFileInfoIn fileIn = new BasSaveFileInfoIn();
                fileIn.setFile(fileDef);
                fileIn.setSaveAction("S");
                so = ServiceBeanUtil.getBean(BasSaveFileInfoService.class).SaveFileInfo(fileIn);
            } catch (BizException bz) {
                so.setRspCode(bz.getCode());
                try {
                    client.removeObject(bucket, (StrUtil.isNotBlank(group) ? (group + "/") : "") + fileId);
                } catch (Exception ignored) {
                }
            } catch (Exception e) {
                so.setRspCode("SYS-0010");
                so.setRspCode("SYS-0010");
                so.setRspDesc("上传文件时发生未知错误：" + e.getMessage());
                try {
                    client.removeObject(bucket, (StrUtil.isNotBlank(group) ? (group + "/") : "") + fileId);
                } catch (Exception ignored) {
                }
            }
        }

        renderJson(so);
    }

    public void del() {
        String fileId = getPara("fileId");

        ServiceOut so = new ServiceOut();
        if (StrUtil.isBlank(fileId)) {
            so.setRspCode("SYS-0008");
            so.setRspDesc("文件id未提示，不能获取文件信息。");
        } else {
            MbasFilDef fileDef = new MbasFilDef();
            fileDef.setFileId(fileId);

            BasViewFileInfoIn vIn = new BasViewFileInfoIn();
            vIn.setFileId(fileId);
            List<MbasFilDef> files = null;
            try {
                files = ServiceBeanUtil.getBean(BasViewFileInfoService.class).viewFileInfo(vIn).getFileList();
            } catch (Exception ignored) {
            }

            if (CollUtil.isEmpty(files)) {
                so.setRspCode("SYS-0008");
                so.setRspDesc("不能获取文件信息,id:" + fileId);
                renderJson(so);
                return;
            }

            BasSaveFileInfoIn fileIn = new BasSaveFileInfoIn();
            fileIn.setFile(fileDef);
            fileIn.setSaveAction("D");

            try {
                so = ServiceBeanUtil.getBean(BasSaveFileInfoService.class).SaveFileInfo(fileIn);

                try {
                    client.removeObject(bucket, fileId);
                } catch (Exception ex) {
                    so.setRspCode("SYS-0010");
                    so.setRspDesc("从服务器删除旧文件失败,id:" + fileId);

                    // 删除的记录还原
                    fileIn = new BasSaveFileInfoIn();
                    fileIn.setFileList(files);
                    fileIn.setSaveAction("U");

                    ServiceBeanUtil.getBean(BasSaveFileInfoService.class).SaveFileInfo(fileIn);
                }
            } catch (BizException bz) {
                so.setRspCode(bz.getCode());
            } catch (Exception e) {
                so.setRspCode("SYS-0010");
                so.setRspDesc("从服务器删除旧文件失败,id:" + fileId);
            }
        }

        renderJson(so);
    }

    /**
     * 请求参数
     * fileId:要访问的文件ID
     * mode:模式，S:文件信息存放在db里
     * del:是否访问完要删除，Y:是
     * group:文件所属组（或称目录）
     * download:1是下载，0是预览
     */
    public void download() {
        String fileId = getPara("fileId");
        String mode = getPara("mode");

        if (StrUtil.isBlank(fileId)) {
            renderText("File id is empty.");
            return;
        }

        OutputStream rspOs = null;
        try {
            List<MbasFilDef> files = null;
            if (StrUtil.equalsIgnoreCase("S", mode)) {
                BasViewFileInfoIn vIn = new BasViewFileInfoIn();
                vIn.setFileId(fileId);
                try {
                    files = ServiceBeanUtil.getBean(BasViewFileInfoService.class).viewFileInfo(vIn).getFileList();
                } catch (Exception ignored) {
                }

                if (CollUtil.isEmpty(files)) {
                    renderText("Fetching file " + fileId + " fail.");
                    return;
                }
            }

            //String group = getPara("group");

            //InputStream fileIs = client.getObject(bucket, StrUtil.isBlank(group) ? fileId : group + "/" + fileId);
            //ObjectStat stat = client.statObject(bucket, StrUtil.isBlank(group) ? fileId : group + "/" + fileId);
            String[] filePath = StrUtil.split(files.get(0).getFilePath(), files.get(0).getFileGroup());
            if (filePath.length != 2) {
                throw new Exception("File info is invalid.");
            }
            String fileMinIoObj = StrUtil.removePrefix(filePath[1], "/");
            //ObjectStat stat = client.statObject(bucket, files.get(0).getFilePath());
            ObjectStat stat = client.statObject(bucket, fileMinIoObj);
            List<String> fileNames = stat.httpHeaders().get("x-amz-meta-filename");
            String fileName = CollUtil.isNotEmpty(fileNames) ? fileNames.get(0) : (CollUtil.isNotEmpty(files) ? files.get(0).getFileName() : null);

            String fileExt = FileUtil.extName(CollUtil.isNotEmpty(files) ? files.get(0).getFileName() : fileName);
            if ((StrUtil.equalsIgnoreCase(fileExt, "png") || StrUtil.equalsIgnoreCase(fileExt, "jpg")) && "0".equals(getPara("download"))) {
                getResponse().setContentType("image/" + (StrUtil.startWithIgnoreCase(fileExt, "jpg") ? "jpeg" : "png"));
            } else if (StrUtil.equalsIgnoreCase(fileExt, "pdf") && "0".equals(getPara("download"))) {
                getResponse().setContentType("application/pdf");
            } else {
                getResponse().setContentType("application/octet-stream");
            }
            getResponse().setHeader("Content-Disposition", "inline; filename=" + fileName);
            rspOs = getResponse().getOutputStream();

            InputStream fileIs = client.getObject(bucket, fileMinIoObj);//client.getObject(bucket, files.get(0).getFilePath());
            if (StrUtil.equalsIgnoreCase(fileExt, "png") || StrUtil.equalsIgnoreCase(fileExt, "jpg")) {
                BufferedImage bImage = ImageUtil.read(fileIs);
                IoUtil.close(fileIs);

                int width = getInt("width", -1);
                if (width <= 0) {
                    width = bImage.getWidth();
                }
                int height = getInt("height", -1);
                if (height <= 0) {
                    height = bImage.getHeight();
                }

                ImageUtil.write(ImageUtil.scale(bImage, width, height),
                        StrUtil.equalsIgnoreCase(fileExt, "png") ? ImageUtil.IMAGE_TYPE_PNG : ImageUtil.IMAGE_TYPE_JPG,
                        rspOs);
            } else {
                IoUtil.copy(fileIs, rspOs);
            }
            IoUtil.close(fileIs);

            if ("Y".equals(getPara("del"))) {
                try {
                    if (CollUtil.isNotEmpty(files)) {
                        BasSaveFileInfoIn sFileInfoIn = new BasSaveFileInfoIn();
                        MbasFilDef file = new MbasFilDef();
                        file.setFileId(fileId);
                        sFileInfoIn.setFile(file);
                        sFileInfoIn.setSaveAction("D");
                        ServiceBeanUtil.getBean(BasSaveFileInfoService.class).SaveFileInfo(sFileInfoIn);
                    }

                    client.removeObject(bucket, fileId);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        } catch (Exception ex) {
            renderText("Download file fail." + ex.getMessage());

            ex.printStackTrace();
            return;
        } finally {
            IoUtil.close(rspOs);
        }

        renderNull();
    }
}

